package cn.cordys.crm.system.excel.listener;

import cn.cordys.common.constants.BusinessModuleField;
import cn.cordys.common.exception.GenericException;
import cn.cordys.common.mapper.CommonMapper;
import cn.cordys.common.util.CommonBeanFactory;
import cn.cordys.common.util.Translator;
import cn.cordys.crm.system.constants.FieldType;
import cn.cordys.crm.system.dto.field.base.BaseField;
import cn.cordys.crm.system.dto.field.base.SubField;
import cn.cordys.excel.domain.ExcelErrData;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import cn.idev.excel.metadata.CellExtra;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author song-cc-rock
 */
public class CustomFieldCheckEventListener extends AnalysisEventListener<Map<Integer, String>> {

	/**
	 * 表头字段集合
	 */
    protected final Map<String, BaseField> fieldMap = new HashMap<>();
    /**
     * 源数据表
     */
    private final String sourceTable;
    private final String fieldTable;
	protected final String currentOrg;
    /**
     * 必填校验
     */
    private final List<String> requires = new ArrayList<>();
    /**
     * 唯一校验&&数据库属性值缓存&&Excel列值缓存
     */
    private final Map<String, BaseField> uniques = new HashMap<>();
    private final Map<String, Set<String>> uniqueCheckSet = new ConcurrentHashMap<>();
    private final Map<String, Set<String>> excelValueCache = new ConcurrentHashMap<>();
	private final CommonMapper commonMapper;
    /**
     * 长度校验
     */
    private final Map<String, Integer> fieldLenLimit = new HashMap<>();
	/**
	 * 错误, 成功信息
	 */
    @Getter
    protected Integer success = 0;
    @Getter
    protected List<ExcelErrData> errList = new ArrayList<>();
	/**
	 * 表头字段集合 && 业务字段集合映射
	 */
	protected Map<Integer, String> headMap;
	protected Map<String, BusinessModuleField> businessFieldMap;
	/**
	 * 错误行号集合
	 */
	@Getter
	protected List<Integer> errRows = new ArrayList<>();
	/**
	 * 子字段引用映射(子字段名称 -> 子表格字段ID)
	 */
	protected final Map<String, String> refSubMap = new HashMap<>();
	/**
	 * 合并单元格信息
	 */
	protected final Map<Integer, List<CellExtra>> mergeCellMap;
	/**
	 * 是否至少有一行数据 && 表头行数
	 */
	protected boolean atLeastOne = false;
	protected int maxHeadRow;

    public CustomFieldCheckEventListener(List<BaseField> fields, String sourceTable, String fieldTable, String currentOrg,
										 Map<Integer, List<CellExtra>> mergeCellMap) {
		for (BaseField field : fields) {
			if (StringUtils.isNotEmpty(field.getResourceFieldId())) {
				continue;
			}
			if (field instanceof SubField subField && CollectionUtils.isNotEmpty(subField.getSubFields())) {
				for (BaseField f : subField.getSubFields()) {
					if (StringUtils.isNotEmpty(f.getResourceFieldId())) {
						continue;
					}
					this.fieldMap.put(f.getName(), f);
					refSubMap.put(f.getName(), subField.getId());
					setCheckLimit(f);
				}
				continue;
			}
			this.fieldMap.put(field.getName(), field);
			setCheckLimit(field);
		}
        this.sourceTable = sourceTable;
        this.currentOrg = currentOrg;
        this.commonMapper = CommonBeanFactory.getBean(CommonMapper.class);
        this.fieldTable = fieldTable;
		this.mergeCellMap = mergeCellMap;
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
		maxHeadRow = context.readWorkbookHolder().getHeadRowNumber();
		if (context.readRowHolder().getRowIndex() != maxHeadRow - 1) {
			return;
		}
        if (headMap == null) {
            throw new GenericException(Translator.get("user_import_table_header_missing"));
        }
        String errHead = checkIllegalHead(headMap);
        if (StringUtils.isNotEmpty(errHead)) {
            throw new GenericException(Translator.getWithArgs("illegal_header", errHead));
        }
        this.headMap = headMap;
        this.businessFieldMap = Arrays.stream(BusinessModuleField.values()).
                collect(Collectors.toMap(BusinessModuleField::getKey, Function.identity()));
        cacheUniqueSet();
    }

    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
		if (data == null) {
			return;
		}
		atLeastOne = true;
		Integer rowIndex = context.readRowHolder().getRowIndex();
		validateRowData(rowIndex, data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (!atLeastOne) {
            throw new GenericException(Translator.get("import.data.cannot_be_null"));
        }
    }

    /**
     * 缓存一些比对值
     */
    private void cacheUniqueSet() {
        if (!uniques.isEmpty()) {
            uniques.values().forEach(field -> {
                if (businessFieldMap.containsKey(field.getInternalKey()) && !refSubMap.containsKey(field.getName())) {
					// 子表格字段不走业务唯一性校验
                    BusinessModuleField businessModuleField = businessFieldMap.get(field.getInternalKey());
                    String fieldName = businessModuleField.getBusinessKey();
                    List<String> valList = commonMapper.getCheckValList(sourceTable, fieldName, currentOrg);
                    uniqueCheckSet.put(field.getName(), new HashSet<>(valList.stream().distinct().toList()));
                } else {
                    List<String> valList = commonMapper.getCheckFieldValList(sourceTable, fieldTable, field.getId(), currentOrg);
                    uniqueCheckSet.put(field.getName(), new HashSet<>(valList));
                }
            });
        }
    }

    /**
     * 校验行数据
     *
     * @param rowIndex 行索引
     * @param rowData  行数据
     */
    private void validateRowData(Integer rowIndex, Map<Integer, String> rowData) {
        StringBuilder errText = new StringBuilder();
        headMap.forEach((k, v) -> {
			if (!isValidateCell(rowIndex, k)) {
				return;
			}
            if (requires.contains(v) && StringUtils.isEmpty(rowData.get(k))) {
                errText.append(v).append(Translator.get("cannot_be_null")).append(";");
            }
            if (uniques.containsKey(v) && !checkFieldValUnique(rowData.get(k), uniques.get(v))) {
                errText.append(v).append(Translator.get("cell.not.unique")).append(";");
            }
            if (fieldLenLimit.containsKey(v) && StringUtils.isNotEmpty(rowData.get(k)) &&
                    rowData.get(k).length() > fieldLenLimit.get(v)) {
                errText.append(v).append(Translator.getWithArgs("over.length", fieldLenLimit.get(v))).append(";");
            }
        });
		if (StringUtils.isNotEmpty(errText)) {
            ExcelErrData excelErrData = new ExcelErrData(rowIndex,
                    Translator.getWithArgs("row.error.tip", rowIndex + 1).concat(" " + errText));
            //错误信息
            errList.add(excelErrData);
			errRows.add(rowIndex);
        } else {
            success++;
        }
    }

	/**
	 * 判断单元格是否需要校验
	 * @param rowIndex 行序号
	 * @param colIndex 列序号
	 * @return 是否需要校验
	 */
	private boolean isValidateCell(int rowIndex, int colIndex) {
		if (mergeCellMap == null) {
			return true;
		}
		List<CellExtra> cellExtras = mergeCellMap.get(rowIndex);
		if (cellExtras != null) {
			for (CellExtra extra : cellExtras) {
				// 属于合并列的区域内
				if (colIndex >= extra.getFirstColumnIndex() && colIndex <= extra.getLastColumnIndex()) {
					// 合并第一行也需校验
					return rowIndex == extra.getFirstRowIndex();
				}
			}
		}
		// 不属于合并单元格，直接校验
		return true;
	}

    /**
     * 检查字段值唯一
     *
     * @param val   值
     * @param field 字段
     *
     * @return 是否唯一
     */
    private boolean checkFieldValUnique(String val, BaseField field) {
        if (StringUtils.isEmpty(val)) {
            return true;
        }
        // Excel 唯一性校验
        excelValueCache.putIfAbsent(field.getId(), ConcurrentHashMap.newKeySet());
        Set<String> valueSet = excelValueCache.get(field.getId());
        if (!valueSet.add(val)) {
            return false;
        }
        // 数据库唯一性校验
        Set<String> uniqueCheck = uniqueCheckSet.get(field.getName());
        return !uniqueCheck.contains(val);
    }

    /**
     * 表头是否非法
     *
     * @param headMap 表头集合
     *
     * @return 是否非法
     */
    private String checkIllegalHead(Map<Integer, String> headMap) {
        for (BaseField field : fieldMap.values()) {
            if (!field.canImport() || Strings.CS.equals(field.getType(), FieldType.TEXTAREA.name())) {
                continue;
            }
            if (!headMap.containsValue(field.getName())) {
                return field.getName();
            }
        }
        return null;
    }

	/**
	 * 设置校验信息
	 * @param field 自定义字段
	 */
	private void setCheckLimit(BaseField field) {
		if (field.needRequireCheck()) {
			requires.add(field.getName());
		}
		if (field.needRepeatCheck()) {
			uniques.put(field.getName(), field);
		}
		if (Strings.CS.equalsAny(field.getType(), FieldType.INPUT.name(), FieldType.INPUT_NUMBER.name(), FieldType.DATE_TIME.name(),
				FieldType.MEMBER.name(), FieldType.DEPARTMENT.name(), FieldType.DATA_SOURCE.name(), FieldType.RADIO.name(),
				FieldType.SELECT.name(), FieldType.PHONE.name(), FieldType.LOCATION.name(), FieldType.INDUSTRY.name())) {
			fieldLenLimit.put(field.getName(), 255);
		}
		if (Strings.CS.equals(field.getType(), FieldType.TEXTAREA.name())) {
			fieldLenLimit.put(field.getName(), 3000);
		}
	}
}
